/*
 * GameFrame.java
 *
 * Created on November 13, 2008, 2:26 PM
 */
package mmtothello;

import java.awt.GridLayout;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.ObjectInputStream;

import backprop.Backprop;

/**
 *
 * @author  mocksuwannatat
 */
public class GameFrame extends javax.swing.JFrame
{

	/** Creates new form GameFrame */
	public GameFrame()
	{
		initComponents();
		initLogic();
	}

	/** This method is called from within the constructor to
	 * initialize the form.
	 * WARNING: Do NOT modify this code. The content of this method is
	 * always regenerated by the Form Editor.
	 */
  // <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
  private void initComponents() {

    jLabel1 = new javax.swing.JLabel();
    lblNumBlack = new javax.swing.JLabel();
    lblNumWhite = new javax.swing.JLabel();
    jButton1 = new javax.swing.JButton();
    jLabel4 = new javax.swing.JLabel();
    jLabel5 = new javax.swing.JLabel();
    cmbBlackThinker = new javax.swing.JComboBox();
    cmbWhiteThinker = new javax.swing.JComboBox();
    pnlBoard = new javax.swing.JPanel();
    lblWhoseMove = new javax.swing.JLabel();
    jLabel6 = new javax.swing.JLabel();
    tfHowManyGames = new javax.swing.JTextField();
    cbAutorun = new javax.swing.JCheckBox();
    jLabel2 = new javax.swing.JLabel();
    cbAutoSwap = new javax.swing.JCheckBox();

    setDefaultCloseOperation(javax.swing.WindowConstants.EXIT_ON_CLOSE);

    jLabel1.setText("# of pieces =");

    lblNumBlack.setText("0");

    lblNumWhite.setText("0");

    jButton1.setText("new game");
    jButton1.addActionListener(new java.awt.event.ActionListener() {
      public void actionPerformed(java.awt.event.ActionEvent evt) {
        jButton1ActionPerformed(evt);
      }
    });

    jLabel4.setText("BLACK's thinker:");

    jLabel5.setText("WHITE's thinker:");

    cmbBlackThinker.setModel(new javax.swing.DefaultComboBoxModel(new String[] { "Item 1", "Item 2", "Item 3", "Item 4" }));

    cmbWhiteThinker.setModel(new javax.swing.DefaultComboBoxModel(new String[] { "Item 1", "Item 2", "Item 3", "Item 4" }));

    org.jdesktop.layout.GroupLayout pnlBoardLayout = new org.jdesktop.layout.GroupLayout(pnlBoard);
    pnlBoard.setLayout(pnlBoardLayout);
    pnlBoardLayout.setHorizontalGroup(
      pnlBoardLayout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING)
      .add(0, 448, Short.MAX_VALUE)
    );
    pnlBoardLayout.setVerticalGroup(
      pnlBoardLayout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING)
      .add(0, 262, Short.MAX_VALUE)
    );

    lblWhoseMove.setText("It's WHITE's move...");

    jLabel6.setText("# of pieces =");

    tfHowManyGames.setText("10");
    tfHowManyGames.addActionListener(new java.awt.event.ActionListener() {
      public void actionPerformed(java.awt.event.ActionEvent evt) {
        tfHowManyGamesActionPerformed(evt);
      }
    });

    cbAutorun.setText("autorun");
    cbAutorun.addActionListener(new java.awt.event.ActionListener() {
      public void actionPerformed(java.awt.event.ActionEvent evt) {
        cbAutorunActionPerformed(evt);
      }
    });

    jLabel2.setText("games");

    cbAutoSwap.setLabel("auto-swap players' orders");
    cbAutoSwap.addActionListener(new java.awt.event.ActionListener() {
      public void actionPerformed(java.awt.event.ActionEvent evt) {
        cbAutoSwapActionPerformed(evt);
      }
    });

    org.jdesktop.layout.GroupLayout layout = new org.jdesktop.layout.GroupLayout(getContentPane());
    getContentPane().setLayout(layout);
    layout.setHorizontalGroup(
      layout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING)
      .add(org.jdesktop.layout.GroupLayout.TRAILING, layout.createSequentialGroup()
        .add(layout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING)
          .add(layout.createSequentialGroup()
            .addContainerGap(24, Short.MAX_VALUE)
            .add(layout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING)
              .add(org.jdesktop.layout.GroupLayout.TRAILING, layout.createSequentialGroup()
                .add(jLabel4)
                .addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED)
                .add(cmbBlackThinker, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, 133, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE)
                .addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED)
                .add(jLabel1)
                .addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED)
                .add(lblNumBlack)
                .add(145, 145, 145))
              .add(layout.createSequentialGroup()
                .add(jLabel5)
                .addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED)
                .add(cmbWhiteThinker, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, 133, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE)
                .addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED)
                .add(jLabel6)
                .addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED)
                .add(lblNumWhite)))
            .add(11, 11, 11))
          .add(layout.createSequentialGroup()
            .add(layout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING)
              .add(pnlBoard, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE)
              .add(layout.createSequentialGroup()
                .addContainerGap()
                .add(lblWhoseMove)))
            .add(77, 77, 77)))
        .add(layout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING)
          .add(layout.createParallelGroup(org.jdesktop.layout.GroupLayout.TRAILING)
            .add(layout.createSequentialGroup()
              .add(cbAutorun)
              .addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED)
              .add(tfHowManyGames, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, 37, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE)
              .add(10, 10, 10)
              .add(jLabel2))
            .add(jButton1, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, 127, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE))
          .add(cbAutoSwap))
        .addContainerGap())
    );
    layout.setVerticalGroup(
      layout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING)
      .add(layout.createSequentialGroup()
        .add(20, 20, 20)
        .add(layout.createParallelGroup(org.jdesktop.layout.GroupLayout.BASELINE)
          .add(jLabel4)
          .add(cmbBlackThinker, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE)
          .add(jLabel1)
          .add(lblNumBlack)
          .add(jButton1))
        .addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED)
        .add(layout.createParallelGroup(org.jdesktop.layout.GroupLayout.BASELINE)
          .add(jLabel5)
          .add(cmbWhiteThinker, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE)
          .add(jLabel6)
          .add(lblNumWhite)
          .add(jLabel2)
          .add(tfHowManyGames, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE)
          .add(cbAutorun))
        .addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED)
        .add(layout.createParallelGroup(org.jdesktop.layout.GroupLayout.TRAILING)
          .add(lblWhoseMove)
          .add(cbAutoSwap))
        .addPreferredGap(org.jdesktop.layout.LayoutStyle.UNRELATED)
        .add(pnlBoard, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
    );

    pack();
  }// </editor-fold>//GEN-END:initComponents

private void jButton1ActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_jButton1ActionPerformed
// TODO add your handling code here:
	newGame();
}//GEN-LAST:event_jButton1ActionPerformed

private void tfHowManyGamesActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_tfHowManyGamesActionPerformed
// TODO add your handling code here:
}//GEN-LAST:event_tfHowManyGamesActionPerformed

private void cbAutorunActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_cbAutorunActionPerformed
// TODO add your handling code here:
}//GEN-LAST:event_cbAutorunActionPerformed

private void cbAutoSwapActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_cbAutoSwapActionPerformed
// TODO add your handling code here:
}//GEN-LAST:event_cbAutoSwapActionPerformed

	/**
	 * @param args the command line arguments
	 */
	public static void main(String args[])
	{
		java.awt.EventQueue.invokeLater(new Runnable()
		{

			public void run()
			{
				new GameFrame().setVisible(true);
			}
		});
	}
  // Variables declaration - do not modify//GEN-BEGIN:variables
  private javax.swing.JCheckBox cbAutoSwap;
  private javax.swing.JCheckBox cbAutorun;
  private javax.swing.JComboBox cmbBlackThinker;
  private javax.swing.JComboBox cmbWhiteThinker;
  private javax.swing.JButton jButton1;
  private javax.swing.JLabel jLabel1;
  private javax.swing.JLabel jLabel2;
  private javax.swing.JLabel jLabel4;
  private javax.swing.JLabel jLabel5;
  private javax.swing.JLabel jLabel6;
  private javax.swing.JLabel lblNumBlack;
  private javax.swing.JLabel lblNumWhite;
  private javax.swing.JLabel lblWhoseMove;
  private javax.swing.JPanel pnlBoard;
  private javax.swing.JTextField tfHowManyGames;
  // End of variables declaration//GEN-END:variables

	private void initLogic()
	{
		this.setResizable(false);
		size = C.DEFAULT_DIMENSION;
		createThinkers();
		board = new OBoard(size);
		createPieces();
		redrawBoard();
	}

	private void createThinkers()
	{
		int numThinkers = 7;
		thinkers = new Thinker[numThinkers + 1];
		int k = 0;
		thinkers[k++] = new GreedyThinker();
		thinkers[k++] = new RandomThinker();
//		thinkers[k++] = new AnotherThinker();
		thinkers[k++] = new SimpleAlphaBetaThinker();
		thinkers[k++] = new NonDeterministicAlphaBetaThinker();
		thinkers[k++] = new RandomOnceAlphaBetaThinker();
		thinkers[k++] = new TestNeuralThinker(this.getBasicBackProp("championshipMoveHistory.saved"), "Champion Neural");
		thinkers[k++] = new TestNeuralThinker(this.getBasicBackProp("genericMoveHistory.saved"), "Generic Neural");
		thinkers[k++] = null;
		cmbWhiteThinker.removeAllItems();
		cmbBlackThinker.removeAllItems();
		for (int i = 0; i < numThinkers; i++)
		{
			String name = thinkers[i].getName();
			cmbWhiteThinker.addItem(name);
			cmbBlackThinker.addItem(name);
		}
		String name = "Human";
		cmbWhiteThinker.addItem(name);
		cmbBlackThinker.addItem(name);
		cmbWhiteThinker.setSelectedIndex(numThinkers);
		cmbBlackThinker.setSelectedIndex(numThinkers);
	}
	
	private Backprop getBasicBackProp(String filename) {
		Backprop def = null;
		FileInputStream fis = null;
		ObjectInputStream in = null;
		try {
			fis = new FileInputStream(filename);
			in = new ObjectInputStream(fis);
			def = (Backprop) in.readObject();
			in.close();
		} catch (IOException ex) {
			ex.printStackTrace();
		} catch (ClassNotFoundException ex) {
			ex.printStackTrace();
		}
		return def;
	}

	private void createPieces()
	{
		pnlBoard.setLayout(new GridLayout(size, size));
		cells = new Cell[size][size];
		for (int r = 0; r < size; r++)
		{
			for (int c = 0; c < size; c++)
			{
				char who = board.get(r, c);
				Cell button = new Cell("" + who);
				//button.add
				//TODO add event handler to button
				final int rr = r;
				final int cc = c;
				button.addMouseListener(new MouseAdapter()
				{

					public void mouseReleased(MouseEvent evt)
					{
						userClickedAtCell(rr, cc);
					}
				});

				cells[r][c] = button;
				pnlBoard.add(button);
			}
		}
		pack();
	}

	private void redrawBoard()
	{
		for (int r = 0; r < size; r++)
		{
			for (int c = 0; c < size; c++)
			{
				Cell cell = cells[r][c];
				char who = board.get(r, c);
				boolean valid = board.canSet(r, c, getPlayer());
				String t = valid
						? "" + board.calculateImmediateFlips(r, c, getPlayer())
						: null;
				cell.youAre(who,
						valid,
						board.justFlipped(r, c),
						t);
			}
		}
		lblNumBlack.setText("" + board.count(C.BLACK));
		lblNumWhite.setText("" + board.count(C.WHITE));

		String text = String.format("It's %s's turn...",
				whiteTurn ? "WHITE" : "BLACK");
		if (!humanThinking)
		{
			text += "     COMPUTER IS THINKING...";

		}
		if (gameOver)
		{
			text = "GAME OVER";
		}
		;
		lblWhoseMove.setText(text);
//    repaint();
	}

	private void userClickedAtCell(int r, int c)
	{
		if (gameOver)
		{
			return;

		}
		if (!humanThinking)
		{
			System.out.println("Grrr! Computer is thinking. Please wait.");
			return;
		}

		char old = board.get(r, c);
		char p = getPlayer();
		if (!board.canSet(r, c, p))
		{
			System.out.println("Grrr! can't put it there.");
			return;
		}
		board.set(r, c, p);
		board.flipAll(r, c);
		switchPlayer();
		checkGameOver();
		playIfItsYourTurn();
		redrawBoard();
	}

	private void checkGameOver()
	{
		if (board.countValidMoves(getPlayer()) <= 0)
		{
			gameOver = true;
		}
	}

	private void switchPlayer()
	{
		whiteTurn = !whiteTurn;
	}

	private char getPlayer()
	{
		return whiteTurn ? C.WHITE : C.BLACK;
	}

	private void newGame()
	{
		// user clicked on New Game button

		// prepare thinkers (each could be null, which means human)
		whiteThinker = thinkers[cmbWhiteThinker.getSelectedIndex()];
		blackThinker = thinkers[cmbBlackThinker.getSelectedIndex()];

		// init board & turn
		board.clear();
		whiteTurn = false;
		gameOver = false;
    
    if (cbAutorun.isSelected()) {
      autorun_mode = true;
      autorun_gameIndex = 1;
      autorun_numGames = new Integer(tfHowManyGames.getText());
      autorun_numWinsB = autorun_numWinsW = 0;
    } else {
      autorun_mode = false;
    }
    
		playIfItsYourTurn();
		redrawBoard();
	}
  
  private void autorun_newGameOrNot() {
    if (!autorun_mode) return;

    // print who won
    int nB, nW;
    String blackName = (String) cmbBlackThinker.getSelectedItem();
    String whiteName = (String) cmbWhiteThinker.getSelectedItem();
    String winnerName = "(draw)";
    nB = board.count(C.BLACK);
    nW = board.count(C.WHITE);
    char winner = '-'; 
    if (nB > nW) { 
      winner = 'B';
      winnerName = blackName;
      autorun_numWinsB ++;
    } else if (nW > nB) {
      winner = 'W';
      winnerName = whiteName;
      autorun_numWinsW ++;
    }
    
    System.out.printf("Game# %d/%d, b#= %d, w#= %d, winner=%c (%s)\n", 
        autorun_gameIndex, autorun_numGames, 
        nB, nW, winner, winnerName);
    

    if (autorun_gameIndex >= autorun_numGames) {
      System.out.printf("#wins for B (%s) = %d, #wins for W (%s) = %d\n", 
          blackName, autorun_numWinsB, whiteName, autorun_numWinsW);
      if (cbAutoSwap.isSelected()) {
        cbAutoSwap.setSelected(false);
        int idb = cmbBlackThinker.getSelectedIndex();
        int idw = cmbWhiteThinker.getSelectedIndex();
        cmbBlackThinker.setSelectedIndex(idw);
        cmbWhiteThinker.setSelectedIndex(idb);
        newGame();
      }
      return;
    }
    autorun_gameIndex++;

    // prepare thinkers (each could be null, which means human)
		whiteThinker = thinkers[cmbWhiteThinker.getSelectedIndex()];
		blackThinker = thinkers[cmbBlackThinker.getSelectedIndex()];

		// init board & turn
		board.clear();
		whiteTurn = false;
		gameOver = false;
		playIfItsYourTurn();
		redrawBoard();
  }

	private Thinker currentThinker()
	{
		return whiteTurn ? whiteThinker : blackThinker;
	}

	private void playIfItsYourTurn()
	{
		if (gameOver)
		{
      autorun_newGameOrNot();
			return;

		}
		if (currentThinker() == null)
		{
			humanThinking = true;
			return;
		}

		humanThinking = false;
		Thread thread = new Thread()
		{

			final OBoard fboard = board;

			public void run()
			{
				Thinker joe = currentThinker();
				char p = getPlayer();
				// this can delay
				RowCol move = joe.nextMove(p, board);
				int r = move.row;
				int c = move.col;
				fboard.set(r, c, p);
				fboard.flipAll(r, c);
				switchPlayer();
				checkGameOver();
				playIfItsYourTurn();
				redrawBoard();
			}
		};
		thread.start();
	}
	private int size;
	private OBoard board = null;
	private Cell[][] cells;
	private boolean whiteTurn = false;
	private Thinker[] thinkers;
	private boolean humanThinking = true;
	private Thinker whiteThinker,  blackThinker;
	private boolean gameOver = false;
  private boolean autorun_mode = false;
  private int autorun_numGames = 1;
  private int autorun_gameIndex = 0;
  private int autorun_numWinsB = 0;
  private int autorun_numWinsW = 0;
}
